package cadtoolbox.model;

import java.io.Serializable;
import java.util.ArrayList;

public class SequenceVertex implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -2149500482426445287L;
	public Integer ID;
	private static ArrayList<Integer> availableIDs = new ArrayList<Integer>();
	public double initialConcentration;
	private double concentration;
	private boolean inhib = false;
	public final String sequence;
	public ArrayList<AbstractInput> inputs;
	
	public SequenceVertex(Integer i){
		this.ID = i;
		initialConcentration = 0;
		concentration = 0;
		this.inputs  = new ArrayList<AbstractInput>();
		this.sequence = "";
	}
	
	public SequenceVertex(Integer i, double init){
		this.ID = i;
		this.initialConcentration = init;
		this.concentration = init;
		this.inputs  = new ArrayList<AbstractInput>();
		this.sequence = "";
	}
	
	public SequenceVertex(Integer i, boolean inhib){
		this(i);
		this.inhib = inhib;
	}
	
	public SequenceVertex(Integer i, String sequence){
		this.ID = i;
		initialConcentration = 0;
		concentration = 0;
		this.inputs  = new ArrayList<AbstractInput>();
		this.sequence = sequence;
	}
	
	public SequenceVertex(Integer i, double init, String sequence){
		this.ID = i;
		this.initialConcentration = init;
		this.concentration = init;
		this.inputs  = new ArrayList<AbstractInput>();
		this.sequence = sequence;
	}
	
	public SequenceVertex(Integer i, boolean inhib, String sequence){
		this(i,sequence);
		this.inhib = inhib;
	}
	
	public void setInitialConcentration(double init){
		this.initialConcentration = init;
		this.concentration = init;
	}
	
	public void setInhib(boolean inhib){
		this.inhib = inhib;
	}
	
	public void reset(){
		this.concentration = this.initialConcentration;
		for(AbstractInput ai : this.inputs){
			ai.reset();
		}
	}
	
	public static ArrayList<Integer> getAvailableIDs(){
		return availableIDs;
	}
	
	public String toString(){
		return (this.inhib?"I":"s")+this.ID;
	}
	
	public String displayName(){
		return ""+(this.ID+1);
	}
	
	public double getConcentration(){
		return this.concentration;
	}
	
	public void setConcentration(double conc){
		this.concentration = conc;
	}
	
	@Override
	public boolean equals(Object o){
		if(o == null){
			return false;
		}
		if(!(o.getClass()==SequenceVertex.class)){
			return false;
		}

		SequenceVertex s = (SequenceVertex) o;
		return (s.ID == this.ID);
	}
	
	public class DNAInput extends AbstractInput implements Serializable{
		public int time;
		public static final double epsilon = 1e-10;
		private int lastSing = -1;
		public int period;
		public double ammount;
		public boolean periodic;
		
		public DNAInput(int time){
			this.time = time;
			this.period = 100;
			this.ammount = 50;
			this.periodic = false;
		}
		
		DNAInput(int time, int period){
			this.time = time;
			this.period = period;
			this.ammount = 50;
			this.periodic = (period > 0);
		}
		
		public String toString(){
			return "Pulse "+(periodic?"starting at time "+time+", period "+period:" at time "+time);
		}

		public int getTime() {
			return time;
		}
		
		public int getPeriod(){
			return period;
		}
		
		public boolean isPeriodic(){
			return periodic;
		}
		
		public void setTime(double time){
			this.time = (int) time;
		}
		
		public void setPeriod(int period){
			this.period = period;
		}
		
		public void setPeriodic(boolean periodic){
			this.periodic = periodic;
		}

		@Override
		public boolean hasSingularity() {
			return (lastSing==-1 || periodic);
		}

		@Override
		public double getNextSingularityTime() {
			if(lastSing == -1){
				return time;
			} else if(periodic){
				return lastSing+period;
			}
			return -1;
		}

		@Override
		public double f(double t) {
			if(t == this.time || this.periodic && ((t % this.period) - (this.time % this.period)) < DNAInput.epsilon){
				return this.ammount;
			}
			return 0;
		}

		@Override
		public void consummeSingularity() {
			if(this.lastSing == -1) {
				this.lastSing = time;
			} else if(periodic){
				this.lastSing += this.period;
			} 
		}
		
		@Override
		public String toMathematica(SequenceVertex seq){
			return "{{"+seq.displayName()+"}, 15*E^(-("+(this.periodic?"Mod[t - "+this.time+", "+this.period+"] - "+(this.period/2):"t - "+this.time)+")^2/600)}";
		}

		@Override
		public void reset() {
			lastSing = -1;
			
		}

	}
}
